<template>
  <v-container fluid>
    <v-row>
      <v-col cols="12" md="6">
        <v-card elevation="2">
          <v-simple-table>
            <template v-slot:default>
              <thead>
                <tr>
                  <th class="text-left">变量名称</th>
                  <th class="text-left">变量类型</th>
                  <th class="text-left">值域</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="prop in properties" :key="prop.variable">
                  <td>{{ prop.variable }}</td>
                  <td>{{ prop.type }}</td>
                  <td>
                    <v-text-field
                      :label="prop.defaultText"
                      v-model="prop.value"
                    ></v-text-field>
                  </td>
                </tr>
              </tbody>
            </template>
          </v-simple-table>
        </v-card>
      </v-col>
    </v-row>
    <v-row>
      <v-col cols="12" md="6">
        <v-textarea
          solo
          name="template"
          @input="autoAnalysisVariables"
          v-model="analysisText"
          rows="20"
          label="请输入模板"
        ></v-textarea>
      </v-col>
      <v-col cols="12" md="6">
        <v-textarea
          solo
          name="generateText"
          readonly
          v-model="generateText"
          rows="20"
          @click="generate"
        ></v-textarea>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
const variablePatterns = [
  {
    name: "数字型值域",
    pattern: /#\{([a-zA-Z_][a-zA-Z\d_]*)\}/,
    value: "0-9",
    calParams: (params) => {
      let tokens = params.split("-").map(x => x.trim()),
        start = parseInt(tokens[0]),
        end = parseInt(tokens[1]),
        ids = [];
      for (let i = start; i <= end; ++i) {
        ids.push(i);
      }
      return ids;
    },
  },
  {
    name: "字符串型值域",
    pattern: /@\{([a-zA-Z_][a-zA-Z\d_]*)\}/,
    value: "1,2,3",
    calParams: (params, fields = {}) => {
      let tokens = params.split(",").map(x => x.trim()), results = [];
      for (let token of tokens) {
        if (token.startsWith("[") && token.endsWith("]")) {
          let name = token.substring(1, token.length - 1);
          results.push(fields[name]);
        } else {
          results.push(token);
        }
      }
      return results;
    },
  },
];

function cycleGenerate(raw, props, index, results, fields = {}) {
  if (index >= props.length) {
    results.push(raw);
    return;
  }
  let prop = props[index], params = prop.ref.calParams(prop.value, fields);
  for (let param of params) {
    let text = raw.replaceAll(prop.matchPattern, param);
    fields[prop.variable] = param;
    cycleGenerate(text, props, index + 1, results, fields);
  }
}

export default {
  data() {
    return {
      properties: [],
      analysisText: "",
      generateText: "",
    };
  },
  methods: {
    generate() {
      this.generateText = "";
      let results = [];
      cycleGenerate(this.analysisText, this.properties, 0, results);
      this.generateText = results.join("");
    },
    autoAnalysisVariables(analysisText) {
      let propertiesMap = new Map();
      for (let prop of this.properties) {
        propertiesMap.set(prop.variable, prop.value);
      }
      this.properties = [];
      let propertiesSet = new Set(),
        i = 0,
        len = variablePatterns.length;
      i < len;
      while (i < len) {
        let variablePattern = variablePatterns[i],
          matchers = analysisText.match(variablePattern.pattern);
        if (matchers) {
          let propVariable = matchers[1],
            processedText = matchers[0];
          if (!propertiesSet.has(processedText)) {
            this.properties.push({
              variable: propVariable,
              type: variablePattern.name,
              defaultText: variablePattern.value.substring(0),
              value: propertiesMap.has(propVariable)
                ? propertiesMap.get(propVariable)
                : variablePattern.value,
              matchPattern: processedText,
              ref: variablePattern,
            });
            propertiesSet.add(processedText);
          }
          analysisText = analysisText.replace(processedText, "");
          continue;
        }
        i++;
      }
    },
  },
};
</script>